const { resolve } = require('path');
const dbPool = require('../sql/sql_pool');
const crypto = require('crypto');

class Controller {
    static #iterations = 310000;
    static #keyLength = 32;

    // 注册
    static async register(username, password) {
        var { salt, hash } = await this.#hashPassword(password);
        var token = await this.#generateSecureToken(salt);

        let sql = `INSERT INTO users (username, salt, hash_password, token) VALUES (?,?,?,?)`;

        try {
            // 执行 SQL 语句
            await dbPool.query(sql, [username, salt, hash, token]);
        }
        catch (err) {
            console.error(err);
            return new Error('注册失败');
        }
        return token;
    }

    // 登录
    static async login(username, password) {
        let sql = `SELECT salt, hash_password FROM users WHERE username =?`;
        let [result] = await dbPool.query(sql, [username]);
        if (result.length === 0) {
            return new Error('登录失败');
        }
        var { salt, hash_password } = result[0];
        // 验证密码
        if (this.#verifyPassword(hash_password, salt, password)) {
            // console.log('登录成功');
            // 生成token
            var token = await this.#generateSecureToken(salt);
            // 更新token
            let updateSql = `UPDATE users SET token = ? WHERE username =?`;
            await dbPool.query(updateSql, [token, username]);
            return token;
        } else {
            return new Error('登录失败, 密码错误');
        }
    }

    // 修改密码
    static async updatePassword(username, oldPassword, newPassword) {
        let sql = `SELECT salt, hash_password FROM users WHERE username =?`;
        let [result] = await dbPool.query(sql, [username]);
        if (result.length === 0) {
            return new Error('登录失败');
        }
        var { salt, hash_password } = result[0];
        // 验证密码
        if (this.#verifyPassword(hash_password, salt, oldPassword)) {
            // 加密新密码
            var { salt, hash } = await this.#hashPassword(newPassword);
            // 生成token
            var token = await this.#generateSecureToken(salt);
            // 更新密码 与 token
            let updateSql = `UPDATE users SET salt = ?, hash_password = ?, token = ? WHERE username =?`;
            await dbPool.query(updateSql, [salt, hash, token, username]);
            return token;
        } else {
            return new Error('修改密码失败, 密码错误');
        }
    }

    // 生成token
    static async #generateSecureToken(salt) {
        return new Promise((resolve, reject) => {
            const randomBytes = crypto.randomBytes(16).toString('hex'); // 生成随机数
            const timestamp = Date.now().toString(); // 当前时间戳

            // 使用HMAC算法生成安全的令牌
            const hmac = crypto.createHmac('sha256', salt);
            hmac.update(randomBytes + timestamp);
            resolve(hmac.digest('hex'));
        });
    }

    // 验证token
    static async verifyToken(username, authToken) {
        return new Promise(async (resolve, reject) => {
            // console.log(` | ${username} | ${authToken} |`);

            let sql = `SELECT token FROM users WHERE username =?`;
            try {
                // 执行 SQL 语句
                let [result] = await dbPool.query(sql, [username]);
                if (result.length === 0) {
                    resolve(false);
                }
                var storedToken = result[0].token;
                resolve(storedToken == authToken);
            } catch (err) {
                console.error(err);
                resolve(false);
            }
        })
    }

    // 创建带盐的哈希
    static async #hashPassword(password) {
        return new Promise((resolve, reject) => {
            // 生成一个随机的盐
            crypto.randomBytes(16, (err, saltBuffer) => {
                if (err) return reject(err);

                const salt = saltBuffer.toString('hex');
                // 使用 pbkdf2 进行密码哈希，这是一种适合密码的健壮哈希算法
                crypto.pbkdf2(password, salt, this.#iterations, this.#keyLength, 'sha256', (err, key) => {
                    if (err) return reject(err);
                    // 返回包含盐和哈希后的密码的对象
                    resolve({ salt, hash: key.toString('hex') });
                });
            });
        });
    }

    // 验证密码
    static async #verifyPassword(storedHash, storedSalt, providedPassword) {
        return new Promise((resolve, reject) => {
            // 使用存储的盐对提供的密码进行哈希
            crypto.pbkdf2(providedPassword, storedSalt, this.#iterations, this.#keyLength, 'sha256', (err, key) => {
                if (err) return reject(err);
                // 比较生成的哈希和存储的哈希
                resolve(storedHash === key.toString('hex'));
            });
        });
    }
}

module.exports = Controller;
